class MyMkvReader: public mkvparser::IMkvReader {
	InputStream strm;
	long long size;

	public:
	MyMkvReader(): strm{}, size{-2} { }
	~MyMkvReader() {
		strm.close();
	}

	MyMkvReader(const MyMkvReader&) =delete;
	MyMkvReader& operator=(const MyMkvReader&) =delete;

	bool open(const std::string& fn) {
		return strm.open(fn);
	}
	bool open(const std::vector<char*>* bufs, size_t bufjunk) {
		return strm.open(bufs, bufjunk);
	}

	int Read(long long pos, long len, unsigned char* buf) override {
		if(len==0)
			return 0;
		strm.seekg(pos);
		if(!strm.good())
			return -1;
		strm.read(reinterpret_cast<char*>(buf), len);
		if(!strm.good())
			return -1;
		return 0;
	}
	int Length(long long* total, long long* avail) override {
		if(size==-2) {
			strm.seekg(0, std::ios::end);
			if(!strm.good()) {
				size=-1;
			} else {
				size=strm.tellg();
			}
		}
		if(size<0)
			return -1;
		if(total)
			*total=size;
		if(avail)
			*avail=size;
		return 0;
	}
};

class WebmReader: public ImageReader {
	const std::string file_name;
	const std::vector<char*>* cache_bufs;
	size_t cache_size;
	CubeType imgType;
	size_t width, height;
	std::vector<unsigned char> firstFrame;
	std::vector<mkvparser::Block::Frame> frames{};
	bool supported;

	~WebmReader() {
	}
	bool close() {

		return true;
	}
	bool getType(CubeType* type) override {
		if(!supported)
			return false;
		*type=imgType;
		return true;
	}

	bool getSizes(int64_t* w, int64_t* h, int64_t* d) override {
		if(!supported)
			return false;
		*w=width;
		*h=height;
		*d=frames.size();
		return true;
	}

	template<typename T> void copyImageImp(unsigned char* plane, int stride, char* ptr, int64_t ystride) {
		for(size_t y=0; y<height; y++) {
			memcpy(ptr+y*ystride, plane+y*stride, width*sizeof(T));
		}
	}
	void copyImage(unsigned char* plane, int stride, char* ptr, int64_t ystride) {
		switch(imgType) {
			case CubeType::U8:
				copyImageImp<uint8_t>(plane, stride, ptr, ystride);
				break;
			case CubeType::U16:
				copyImageImp<uint16_t>(plane, stride, ptr, ystride);
				break;
			default:
				break;
		}
	}
	bool readData(char* ptr, int32_t x0, int32_t y0, int32_t z0, int64_t ystride, int64_t zstride) override {
		if(!supported)
			return false;
		ptr+=x0*bytes_per_voxel(imgType)+y0*ystride+z0*zstride;
		copyImage(&firstFrame[0], width*bytes_per_voxel(imgType), ptr, ystride);

#pragma omp parallel
		{
			auto id=omp_get_thread_num();
			auto num=omp_get_num_threads();
			size_t ileft=1;
			if(id>0)
				ileft=1+((frames.size()-1)*id+num-1)/num;
			size_t iright=frames.size();
			if(id<num-1)
				iright=1+((frames.size()-1)*(id+1)+num-1)/num;
			if(ileft<iright) {
				vpx_codec_err_t err;
				std::vector<uint8_t> buff;
				MyMkvReader reader;
				if(cache_bufs) {
					reader.open(cache_bufs, cache_size);
				} else {
					reader.open(file_name);
				}

				vpx_codec_ctx_t ctx;
				vpx_codec_dec_cfg_t cfg;
				cfg.w=width;
				cfg.h=height;
				cfg.threads=1;
				err=vpx_codec_dec_init(&ctx, vpx_codec_vp9_dx(), &cfg, 0);
				if(err!=VPX_CODEC_OK)
					throwError("Failed to init decoder: ", vpx_codec_err_to_string(err));
				bool got_img=false;
				size_t imgi=ileft;
				for(size_t i=ileft; i<iright || got_img; i++) {
					if(i<iright) {
						auto len=frames[i].len;
						if(len>static_cast<long long>(buff.size()))
							buff.resize(len);
						if(frames[i].Read(&reader, &buff[0]))
							throwError("Failed to read frame.");
						err=vpx_codec_decode(&ctx, &buff[0], len, nullptr, 50);
						if(err!=VPX_CODEC_OK)
							throwError("Failed to decode frame: ", vpx_codec_err_to_string(err));
					} else {
						err=vpx_codec_decode(&ctx, nullptr, 0, nullptr, 50);
						if(err!=VPX_CODEC_OK)
							throwError("Failed to flush frame: ", vpx_codec_err_to_string(err));
					}

					got_img=false;
					vpx_codec_iter_t iter=nullptr;
					while(auto img=vpx_codec_get_frame(&ctx, &iter)) {
						got_img=true;
						copyImage(img->planes[VPX_PLANE_Y], img->stride[VPX_PLANE_Y], ptr+imgi*zstride, ystride);
						imgi++;
					}
				}
				err=vpx_codec_destroy(&ctx);
				if(err!=VPX_CODEC_OK)
					throwError("Failed to destroy decoder: ", vpx_codec_err_to_string(err));
			}
		}
		return true;
	}

	bool probe() {
		MyMkvReader reader;
		if(cache_bufs) {
			reader.open(cache_bufs, cache_size);
		} else {
			reader.open(file_name);
		}

		mkvparser::EBMLHeader header;
		long long off=0;
		if(header.Parse(&reader, off)<0)
			return false;
		mkvparser::Segment *segment;
		if(mkvparser::Segment::CreateInstance(&reader, off, segment))
			return false;
		std::unique_ptr<mkvparser::Segment> _segment{segment};
		if(segment->Load()<0)
			return false;

		const mkvparser::Tracks* tracks=segment->GetTracks();
		const mkvparser::VideoTrack* video_track=nullptr;
		for(unsigned long i=0; i<tracks->GetTracksCount(); ++i) {
			const mkvparser::Track* track=tracks->GetTrackByIndex(i);
			if(track->GetType()==mkvparser::Track::kVideo
					&& std::string{"V_VP9"}==track->GetCodecId()) {
				video_track=static_cast<const mkvparser::VideoTrack*>(track);
				break;
			}
		}
		if(!video_track)
			return false;

		auto video_track_index=video_track->GetNumber();
		const mkvparser::Cluster* cluster=segment->GetFirst();
		const mkvparser::Block* block=nullptr;
		const mkvparser::BlockEntry* block_entry=nullptr;
		int block_frame_index=0;
		bool eos=false;
		while(true) {
			bool be_eos=false;
			do {
				long status=0;
				bool get_new_block=false;
				if(!block_entry && !be_eos) {
					status=cluster->GetFirst(block_entry);
					get_new_block=true;
				} else if(be_eos || block_entry->EOS()) {
					cluster=segment->GetNext(cluster);
					if(!cluster || cluster->EOS()) {
						eos=true;
						break;
					}
					status=cluster->GetFirst(block_entry);
					be_eos=false;
					get_new_block=true;
				} else if(!block || block_frame_index==block->GetFrameCount()
						|| block->GetTrackNumber()!=video_track_index) {
					status=cluster->GetNext(block_entry, block_entry);
					if(!block_entry || block_entry->EOS()) {
						be_eos=true;
						continue;
					}
					get_new_block=true;
				}
				if(status||!block_entry) {
					eos=true;
					break;
				}
				if(get_new_block) {
					block=block_entry->GetBlock();
					if(!block) {
						eos=true;
						break;
					}
					block_frame_index=0;
				}

			} while(be_eos || block->GetTrackNumber()!=video_track_index);
			if(eos)
				break;

			auto frame=block->GetFrame(block_frame_index++);
			frames.push_back(frame);
		}
		if(frames.size()<=0)
			return false;
		std::vector<uint8_t> frm_data(frames[0].len);
		if(frames[0].Read(&reader, &frm_data[0]))
			throwError("Failed to read frame.");

		vpx_codec_err_t err;
		vpx_codec_ctx_t ctx;
		vpx_codec_dec_cfg_t cfg;
		cfg.w=width=video_track->GetWidth();
		cfg.h=height=video_track->GetHeight();
		cfg.threads=1;
		err=vpx_codec_dec_init(&ctx, vpx_codec_vp9_dx(), &cfg, 0);
		if(err!=VPX_CODEC_OK)
			throwError("Failed to init decoder: ", vpx_codec_err_to_string(err));
		err=vpx_codec_decode(&ctx, &frm_data[0], frames[0].len, 0, 50);
		if(err!=VPX_CODEC_OK)
			throwError("Failed to decode frame: ", vpx_codec_err_to_string(err));
		err=vpx_codec_decode(&ctx, nullptr, 0, nullptr, 50);
		if(err!=VPX_CODEC_OK)
			throwError("Failed to flush frame: ", vpx_codec_err_to_string(err));
		vpx_codec_iter_t iter=nullptr;
		if(auto img=vpx_codec_get_frame(&ctx, &iter)) {
			switch(img->fmt) {
				case VPX_IMG_FMT_I420:
				case VPX_IMG_FMT_YV12:
					imgType=CubeType::U8;
					break;
				case VPX_IMG_FMT_I42016:
					imgType=CubeType::U16;
					break;
				default:
					return false;
			}
			auto bpv=bytes_per_voxel(imgType);
			firstFrame.resize(width*height*bpv);
			for(size_t y=0; y<height; y++) {
				memcpy(&firstFrame[y*width*bpv], img->planes[VPX_PLANE_Y]+y*img->stride[VPX_PLANE_Y], width*bpv);
			}
		} else {
			return false;
		}
		err=vpx_codec_destroy(&ctx);
		if(err!=VPX_CODEC_OK)
			throwError("Failed to destroy decoder: ", vpx_codec_err_to_string(err));
		return true;
	}
	//
	//////////////////////////////////
	//...
	//type 1stframe
	//size parse+1stframe+loop all frame
	//*data frame+decoder

	//
	//
	//





	public:
	WebmReader(const std::string& fn, const std::vector<char*>* bufs, size_t size):
		file_name{fn}, cache_bufs{bufs}, cache_size{size},
		imgType{CubeType::Unknown}, width{0}, height{0},
		firstFrame{}, frames{}, supported{false}
	{
		supported=probe();
	}
	WebmReader(const std::string& fn):
		file_name{fn}, cache_bufs{nullptr}, cache_size{0},
		imgType{CubeType::Unknown}, width{0}, height{0},
		firstFrame{}, frames{}, supported{false}
	{
		supported=probe();
	}



};

